﻿function ImmutableBag(addeditems, unique) {
    this.items = [];
    for (var i = 0; i < addeditems.length; i++) {
        this.items.push(addeditems[i]);
    }
     

    this.contain = function (item) {
        for (var i = 0; i < this.items.length; i++) {
            if (this.items[i] == item) {
                return true;
            }
        }

        return false;
    }

    this.clone = function () {
        return new ImmutableBag(this.items, unique);
    }

    this.add = function (newItems) {
        var newBag = this.clone();
        
        for (var i = 0; i < newItems.length; i++) {
            if (!unique || !newBag.contain(newItems[i])) {
                newBag.items.push(newItems[i]);
            }
        }          
        
        return newBag;
    }

    this.remove = function (item) {
        var newBag = this.clone();
        for (var i = 0; i < newBag.items.length; i++) {
            if (newBag.items[i] == item) {
                newBag.items.splice(i, 1);
                return newBag;
            }
        }
        return newBag;
    }
}

function beginGame(width, height, screen, items, simpleItems) {
    var step = "begin";    
    var item = null;
    var countErased = 0;

    var burned = [];      

    var map = new Array(width);
    for (var x = 0; x < width; x++) {
        map[x] = new Array(height);
    }

    var isPause = false;
    var forNewPlayer = false;

    $("#TurnOnNewPlayerMode").click(function () {
        forNewPlayer = true;
        $("#formForSaveResult").html("<input type='text' id='Points' readonly='readonly' style='width:100px' />");
        $(this).hide();
    });

    $("#pauseButton").click(function() {
        if (!isPause) {
            isPause = true;
            screen.fadeOut();
        } else {
            isPause = false;
            screen.show();
        }
    })

    $("#saveButton").click(function () {
        if ($("#Points").val() == "") {
            alert("Зачем 0 очков то сохранять?");
            return false;
        }

        if ($("#Author").val() == "") {
            alert("Имя забыл указать!");
            return false;
        }

        var mapItems = [];

        for (var y = 0; y < height; y++) {
            for (var x = 0; x < width; x++) {
                mapItems.push(map[x][y] != undefined ? map[x][y].item.id : 0);
            }
        }

        $("#Data").val(JSON.stringify({ burned: burned, map: mapItems }));
    });

    function setPosition() {
        item.drawItem.css({
            left: (32 * item.x) + "px",
            top: (32 * item.y) + "px"
        });
    }

    var founded = [];

    var toX = undefined;   
    /*
    screen.mousemove(function (e) {
        var offset = $(this).offset();
        var mx = e.clientX - offset.left;
        var my = e.clientY - offset.top;
        var toX = Math.floor(mx / 32);
        var toY = Math.floor(my / 32);

       
        for (var y = toY; y < height; y++) {                       
            $("<img class='tetrisItem' />")
                .attr("src", bombUrl)
                .css({
                    left: (32 * toX) + "px",
                    top: (32 * y) + "px"
                })
                .appendTo(screen)
                .fadeOut('slow', function () { $(this).remove(); });  
        }
        
    });*/

    function XYtoKey(x, y) {
        return y * width + x;
    }

    function keyToXY(key) {
        return { x: key % width, y: Math.floor(key / width) }
    }

    function mapByKey(key) {
        return map[key % width][Math.floor(key / width)];
    }

    function can(key) {
        var x = key % width;
        var y = Math.floor(key / width);

        var items = [];
        if (x - 1 >= 0) items.push(XYtoKey(x - 1, y));        
        if (x + 1 <= width - 1) items.push(XYtoKey(x + 1, y));
        if (y - 1 >= 0) items.push(XYtoKey(x, y - 1));
        if (y + 1 <= height - 1) items.push(XYtoKey(x, y + 1));
        return items;
    }

    function checkRecipe(recipe, cells, visitedcells) {
        if (recipe.items.length == 0) {            
            return visitedcells;
        }
        for (var i = 0; i < cells.items.length; i++) {
            var cellId = cells.items[i];
            if (!visitedcells.contain(cellId))
            {                
                var cell = mapByKey(cellId);
                if (cell != undefined) {
                    var itemId = cell.item.id;
                    if (recipe.contain(itemId)) {
                        var r = checkRecipe(recipe.remove(itemId), cells.add(can(cellId)), visitedcells.add([cellId]));
                        if (r != undefined) {
                            return r;
                        }
                    }
                }
            }
        }
        return undefined;
    }
    
    // TEST
    var tests = [];

    

    var changedItems = [];
           

    function func(canNext) {
        var interval = 100;

        if (isPause) {
            setTimeout(func, interval);
            return;
        }

        switch (step) {
            case "begin":

                if (map[width / 2][0] == undefined) {

                    toX = undefined;

                    item = {};
                    item.x = width / 2;
                    item.y = 0;
                    var itemTypeRnd = Math.random();

                    $(".tetrisItemSelected").removeClass("tetrisItemSelected");

                    var itemId = "";
                    if (itemTypeRnd < 0.95) {                    
                        var rndIndex = Math.round(Math.random() * simpleItems.length);
                        if (rndIndex >= simpleItems.length) rndIndex = simpleItems.length - 1;
                        var rnd = simpleItems[rndIndex];
                        if (tests.length > 0) {
                            rnd = tests.pop();
                        }                        
                        itemId = rnd;

                        if (forNewPlayer) {
                            var it = items[itemId];
                            for (var y = 0; y < height; y++) {
                                for (var x = 0; x < width; x++) {
                                    var cell = map[x][y];
                                    if (cell != undefined && cell.drawItem != undefined) {
                                        for (var ri = 0; ri < it.recipes.length; ri++) {
                                            for (var i = 0; i < it.recipes[ri].items.items.length; i++) {
                                                if (it.recipes[ri].items.items[i] == cell.item.id) {
                                                    cell.drawItem.addClass("tetrisItemSelected");
                                                }
                                            }
                                        }
                                    }
                                }
                            }                        
                        }
                    } else if (itemTypeRnd < 0.98) {
                        itemId = "bomb";
                    } else{
                        itemId = "fire";
                    }                        
                    
                    item.item = items[itemId];

                    item.drawItem = $("<img class='tetrisItem' />")
                        .attr("src", item.item.url)
                        .attr("TitleUrl", item.item.TitleUrl)
                        .appendTo(screen);
                    addTooltip(item.drawItem);
                   
                    setPosition();
                    step = "down";
                } else {
                    step = "endgame";
                }
                break;

            case "down":
                if (forNewPlayer) {
                    interval = 2000;
                } else {
                    interval = 1000 - countErased * 10;
                    if (interval < 500) interval = 500;
                }
                if (toX != undefined) {
                    step = "speedHor";
                    interval = 100;
                } else if (item.y + 1 < height) {
                    if (map[item.x][item.y + 1] == undefined) {
                        item.y = item.y + 1;
                        setPosition();
                        step = "down";
                    } else {
                        map[item.x][item.y] = item;
                        step = "end";
                    }
                } else {
                    map[item.x][item.y] = item;
                    step = "end";
                }

                break;
            case "speedHor":
                if (item.x < toX && map[item.x + 1][item.y] == undefined) {
                    item.x += 1;
                    setPosition();
                } else if (item.x > toX && map[item.x - 1][item.y] == undefined) {
                    item.x -= 1;
                    setPosition();
                } else {
                    step = "speedDown";
                }
                break;
            case "speedDown":
                if (item.x < toX && map[item.x + 1][item.y] == undefined) {
                    item.x += 1;
                    setPosition();
                } else if (item.x > toX && map[item.x - 1][item.y] == undefined) {
                    item.x -= 1;
                    setPosition();
                }

                if (item.y + 1 < height) {
                    if (map[item.x][item.y + 1] == undefined) {
                        item.y = item.y + 1;
                        setPosition();
                        step = "speedDown";
                    } else {
                        map[item.x][item.y] = item;
                        step = "end";
                    }
                } else {
                    map[item.x][item.y] = item;
                    step = "end";
                }
                break;
            case "end":
                changedItems.push(XYtoKey(item.x, item.y));
                
            case "clear":
                // получаем следующий элемент               
                var nextItem = undefined;
                while (changedItems.length > 0 && nextItem == undefined)
                {
                    var next = changedItems.pop();
                    nextItem = mapByKey(next);
                };

                if (nextItem != undefined) {                    

                    var burnedInfo = {
                        items: [],
                        assembleItems: [],
                        points: 0
                    };

                    // если элемент существует, то пытаемся его проверить на сжигание
                    if (nextItem.item.id == "bomb") {
                        for (var x = nextItem.x - 1; x <= nextItem.x + 1; x++) {
                            for (var y = nextItem.y - 1; y <= nextItem.y + 1; y++) {
                                if (x >= 0 && x <= width - 1 && y >= 0 && y <= height - 1) {
                                    /*var isOutCircle = x == nextItem.x - 2 && y == nextItem.y - 2
                                        || x == nextItem.x + 2 && y == nextItem.y - 2
                                        || x == nextItem.x - 2 && y == nextItem.y + 2
                                        || x == nextItem.x + 2 && y == nextItem.y + 2;

                                    if (!isOutCircle) {*/
                                    var cell = map[x][y];
                                    if (cell != undefined) {
                                        burnedInfo.items.push(cell);
                                    }
                                    //}*/
                                }
                            }
                        }
                    } else if (nextItem.item.id == "fire") {
                        for (var y = nextItem.y; y < height; y++) {
                            var cell = map[nextItem.x][y];
                            if (cell != undefined) {
                                burnedInfo.items.push(cell);
                            }
                        }
                    } else { 
                        var found = [];

                        var cells = new ImmutableBag(can(XYtoKey(nextItem.x, nextItem.y)), true);
                        var visitedcells = new ImmutableBag([XYtoKey(nextItem.x, nextItem.y)], true);

                        // ищем рецепты
                        for (var i = 0; i < nextItem.item.recipes.length; i++) {
                            var r = checkRecipe(nextItem.item.recipes[i].items.remove(nextItem.item.id), cells, visitedcells);
                            if (r != undefined) {
                                found.push({ r: nextItem.item.recipes[i], cells: r });
                            }
                        }

                        if (found.length > 0) {
                            // выбираем самый длинный :)
                            var maxRecipe = undefined;
                            for (var i = 0; i < found.length; i++) {
                                if (maxRecipe == undefined || found[i].r.items.items.length > maxRecipe.r.items.items.length) {
                                    maxLen = found[i].r.items.items.length;
                                    maxRecipe = found[i];
                                }
                            }

                            // указываем, что надо сжечь
                            for (var i = 0; i < maxRecipe.cells.items.length; i++) {
                                burnedInfo.items.push(mapByKey(maxRecipe.cells.items[i]));
                            }
                            burnedInfo.points += burnedInfo.items.length;

                            // собранный элемент
                            burnedInfo.assembleItems.push({
                                item: items[maxRecipe.r.resultId],
                                x: nextItem.x,
                                y: nextItem.y
                            });
                        }
                    } 

                    if (burnedInfo.items.length > 0) {                       

                        // анимация уничтожанных элементов
                        for (var i = 0; i < burnedInfo.items.length; i++) {                           
                            var cell = burnedInfo.items[i];

                            if (cell.drawItem != undefined) {
                                cell.drawItem.fadeOut('slow', function () { $(this).remove(); });
                            }
                            map[cell.x][cell.y] = undefined;
                        }

                        // очки
                        countErased += burnedInfo.points;                        
                        $("#Points").val(countErased);

                        // анимация собранных элементов
                        for (var i = 0; i < burnedInfo.assembleItems.length; i++) {
                            burned.push(burnedInfo.assembleItems[i].item.id);

                            $("<img class='tetrisItem' />")
                                .attr("src", burnedInfo.assembleItems[i].item.url)
                                .appendTo(screen)
                                .css({
                                    left: (32 * burnedInfo.assembleItems[i].x).toString() + "px",
                                    top: (32 * burnedInfo.assembleItems[i].y).toString() + "px"
                                })
                                .animate({
                                    opacity: 0.25,
                                    top: '-=50',
                                }, 4000, function () {
                                    $(this).remove();
                                });
                        }                  
                        

                        // gravity
                        for (var i = 0; i < burnedInfo.items.length; i++) {
                            var key = burnedInfo.items[i];
                            
                            for (var y = key.y; y > 1; y--) {
                                var cell = map[key.x][y] = map[key.x][y - 1];
                                if (cell != undefined) {
                                    changedItems.push(XYtoKey(key.x, y));
                                    cell.x = key.x;
                                    cell.y = y;

                                    cell.drawItem.css({
                                        left: (32 * cell.x) + "px",
                                        top: (32 * cell.y) + "px"
                                    });
                                }
                            }

                            map[key.x][0] = undefined;
                        }
                    }

                    step = "clear";
                } else {
                    step = "begin";
                }
                break;          
            case "endgame":                
                $("#finishDialog").dialog({
                    width: 600, height: 250,
                    buttons: {
                        "Закрыть": function () { $(this).dialog("close"); }
                    }
                });
                return;

        }
        
        setTimeout(func, interval);
        
    };

    screen.click(function (e) {
        var offset = $(this).offset();
        var mx = e.clientX - offset.left;

        toX = Math.floor(mx / 32);
        if (toX >= width) toX = width - 1;

        //func(false);
    });

    setTimeout(func, 100);
}

function recipe(resultId, items) {
    return {
        resultId: resultId,
        items: new ImmutableBag(items, false)
    };
}